import { Distribution, IDistribution } from '.';
import { ITreeHolder, TreeHolder } from '../reception/TreeHolder';
import { model, schema } from '../../../../base';
import { IDistributionTask } from '../../../thing/standard/distributiontask';
import { IReceptionProvider } from '../reception/IReceptionProvider';
import { Workbook } from 'exceljs';
import _ from 'lodash';
import { ISession } from '@/ts/core';
import { getStatus, statusMap } from '../reception/status';
import { NodeType } from '@/ts/base/enum';
import { nextTick } from '@/ts/base/common/timer';

export interface IReportDistribution
  extends IDistribution<model.ReportDistributionContent>,
    IReceptionProvider {
  /** 持有树 */
  holder: ITreeHolder;
  /** 通过会话接收任务 */
  receive(
    session: ISession,
    nodes: schema.XReportTreeNode[],
  ): Promise<[schema.XReception[], model.ValidateErrorInfo[]]>;
  /**
   * 获取根节点
   * @param belongId 当前单位id，不传查完整树
   */
  findReportRootNode(belongId?: string): Promise<schema.XReportTreeNode[]>;
  /** 导出上报状态 */
  exportReceptionStatus(): Promise<File>;
  /** 返回一个查群私有集合的`IReceptionProvider` */
  getPrivateProvider(): IReceptionProvider;
}

export class ReportDistribution
  extends Distribution<model.ReportDistributionContent>
  implements IReportDistribution
{
  constructor(task: IDistributionTask, metadata: schema.XDistribution) {
    super(task, metadata);
    this.holder = new TreeHolder(this.data.treeId, metadata, this.target.directory);
  }
  holder: ITreeHolder;
  get data() {
    return this.metadata.content as model.ReportDistributionContent;
  }

  async receive(session: ISession, nodes: schema.XReportTreeNode[]) {
    const work = await this.findWork(session);
    const receptions: schema.XReception[] = [];
    const errors: Error[] = [];
    for (const node of nodes) {
      let match = {
        taskId: this.task.id,
        period: this.period,
      } as schema.XReception;
      const receptionColl = this.target.space.resource.receptionColl;
      const result = await receptionColl.loadSpace({
        options: { match: { ...match, 'content.treeNode.id': node.id } },
      });
      if (result.length > 0) {
        const userId = result[0].receiveUserId;
        if (userId != this.target.userId) {
          const user = this.target.user.user!.findShareById(userId)?.name || userId;
          console.warn(`节点 ${node.name} 的任务已被用户 ${user} 接收`);
        } else {
          console.warn(`任务接收 ${this.task.name} ${this.period} 已存在`);
        }
      }
      const application = work.application.metadata;
      let reception = await receptionColl.replace(
        {
          ...(result.length > 0 && result[0]),
          ...match,
          sessionId: work.metadata.shareId,
          belongId: this.task.spaceId,
          periodType: this.periodType,
          distId: this.id,
          typeName: '接收任务',
          name: this.metadata.name,
          receiveUserId: this.target.userId,
          content: {
            type: model.TaskContentType.Report,
            directoryId: application.directoryId,
            workId: work.metadata.sourceId ?? work.metadata.id,
            treeNode: node,
          },
        },
      );
      if (reception) {
        receptions.push(reception);
        await receptionColl.notity({ data: reception, operate: 'replace' });
      }
    }

    const validateInfo = errors.map((e) => {
      return {
        errorCode: 'ERROR',
        message: e.message,
        errorLevel: 'error',
      } as model.ValidateErrorInfo;
    });
    return [receptions, validateInfo] as [schema.XReception[], model.ValidateErrorInfo[]];
  }

  async findReportRootNode(belongId?: string): Promise<schema.XReportTreeNode[]> {
    const allows = [model.TaskContentType.Report, model.TaskContentType.Closing];
    if (!allows.includes(this.metadata.content.type)) {
      return [];
    }

    const options: model.LoadOptions<schema.XReportTreeNode> = {
      options: {
        match: {
          treeId: this.treeId,
        },
      },
    };

    if (!belongId) {
      Object.assign(options.options!.match, {
        _or_: [
          // 找出parentId不存在的视为根节点
          { parentId: '' },
          { parentId: null },
          { parentId: { _exists_: false } },
        ],
      });
    } else {
      Object.assign(options.options!.match, {
        belongId,
      });
    }

    const roots = await this.nodeColl.loadSpace(options);

    // 尝试找出最顶级的那个汇总节点（如果有汇总）
    let root = roots[0];
    let summaryNodes = roots.filter((n) => n.nodeType == NodeType.Summary);
    if (summaryNodes.length > 0) {
      root = summaryNodes[0];
      const others = summaryNodes.slice(1);
      for (const other of others) {
        if (root.parentId == other.id) {
          root = other;
        }
      }
    }

    return [root];
  }

  async findReportReceptions(
    nodeIds: string[],
    fromPrivate = false,
  ): Promise<Dictionary<schema.XReception | null>> {
    if (this.metadata.content.type != model.TaskContentType.Report) {
      return {};
    }

    const ret: Dictionary<schema.XReception | null> = {};

    const coll = fromPrivate
      ? this.target.resource.receptionColl
      : this.target.resource.genTargetColl<schema.XReception>(
          '-' + this.target.resource.receptionColl.collName,
        );

    let res: schema.XReception[] = [];
    const chunks = _.chunk(nodeIds, 5000);
    for (const chunk of chunks) {
      res = res.concat(
        await coll.loadSpace({
          options: {
            match: {
              'content.treeNode.id': {
                _in_: chunk,
              },
              period: this.metadata.period,
              taskId: this.metadata.taskId,
            },
          },
        }),
      );
      // 该接口请求非常非常慢，等待浏览器空闲
      await nextTick();
    }

    let ids = [...nodeIds];
    for (const reception of res) {
      const nodeId = (reception.content as model.ReportStatus).treeNode.id;
      ids.splice(ids.indexOf(nodeId), 1);
      ret[nodeId] = reception;
    }
    for (const nodeId of ids) {
      ret[nodeId] = null;
    }
    return ret;
  }

  getPrivateProvider(): IReceptionProvider {
    return {
      findReportReceptions: (nodeIds) => {
        return this.findReportReceptions(nodeIds, true);
      },
    };
  }

  async exportReceptionStatus(belongId?: string): Promise<File> {
    const roots = await this.findReportRootNode(belongId);
    if (roots.length == 0) {
      throw new Error('找不到树根节点！');
    }

    let workbook = new Workbook();
    const sheet = workbook.addWorksheet('上报状态');
    sheet.addRow(['节点名称', '节点类型', '上报状态']);

    const t = await this.holder.loadTree();
    if (t) {
      let [tree] = await t.loadDistributionTree(roots[0], this);

      function dfs(node: model.ReportTaskTreeNodeView, level = 0) {
        const indent = level > 0 ? _.repeat('　', level) : '';
        let row = [indent + node.name, node.nodeTypeName, '未接收'];

        let status = node.taskStatus || getStatus(node.reception);
        row[2] = statusMap[status].label;

        sheet.addRow(row);

        for (const child of node.children) {
          dfs(child, level + 1);
        }
      }

      for (const node of tree) {
        dfs(node);
      }
    }

    const buffer = await workbook.xlsx.writeBuffer();
    const fileName = `${this.metadata.period} ${this.task.name}上报状态.xlsx`;
    return new File([buffer], fileName, { type: 'application/octet-stream' });
  }
}
